import java.util.ArrayList;

import sim.engine.*;
import ec.util.*;
import sim.util.*;
import sim.field.grid.*;

/* 
 * 	Epstein model converted from NetLogo in to MASON. 
 *  Initiate model and add agents to the grid. 
 *  
 *  This version allows you to create two groups of civilians with different legitimacies.
 */

public class EpsteinGrid extends SimState{
	private static final long serialVersionUID = 1L;
	
	public int neighborhood = 1;
	public int gridHeight = 32;
	public int gridWidth = 32;
	
	public SparseGrid2D grid = new SparseGrid2D(gridWidth, gridHeight);
	public int numCops = 40;
	public int numCivilians1 = 700;
	public int numCivilians2 =0;
	public int quietCount = 0;
	public int activeCount = 0;
	public int jailCount = 0;
	
	public double govtLegitimacyMin1 = 0.82;
	public double govtLegitimacyMax1 = 0.82;
	
	public double govtLegitimacyMin2 = 0.82;
	public double govtLegitimacyMax2 = 0.82;
	
	public int civilianVision = 7;
	public int copVision = 7;
	
	public double copProbability = 0.04;
	public double civilianProbability = 0.7;
	
	public int maxJailTerm = 30;
	public double threshold = 0.1;
	public double group = 0.01;

	public boolean movement = true;
	public boolean smartMovement = false;


	public EpsteinGrid(long seed) {
		super(new MersenneTwisterFast(seed), new Schedule());
	}

	public void start(){
		quietCount = 0;
		activeCount = 0;
		jailCount = 0;
		
		super.start(); //clear out the schedule
		
		grid = new SparseGrid2D(gridWidth, gridHeight);
		
		int copCounter = 0;
		int numCivilians1Counter = 0;
		int numCivilians2Counter = 0;
		for(int x = 0; x < gridWidth; x++){
			for(int y = 0; y < gridHeight; y++){
				double riskAversion = 0;
				double perceivedHardship = 0;
				double govtLegitimacy = 0;
				
				Steppable agent = null;
				
				if(copCounter < numCops){
					agent = new Cop(new Int2D(x, y));				
					schedule.scheduleRepeating(agent);
					copCounter++;
				}
				else if(numCivilians1Counter < numCivilians1){
					riskAversion = random.nextDouble();
					perceivedHardship = random.nextDouble();
					if(govtLegitimacyMin1 == govtLegitimacyMax1){
						govtLegitimacy = govtLegitimacyMin1;
					}
					else{
						boolean generating = true;
						while(generating){
							govtLegitimacy = random.nextDouble();
							if((govtLegitimacy > govtLegitimacyMin1) && (govtLegitimacy < govtLegitimacyMax1)){
								generating = false;
							}
						}
					}
					agent = new Civilian(new Int2D(x, y), riskAversion, perceivedHardship, govtLegitimacy);
					quietCount++;
					schedule.scheduleRepeating(agent);
					numCivilians1Counter++;
				}
				else if(numCivilians2Counter < numCivilians2){
					riskAversion = random.nextDouble();
					perceivedHardship = random.nextDouble();
					if(govtLegitimacyMin2 == govtLegitimacyMax2){
						govtLegitimacy = govtLegitimacyMin2;
					}
					else{	
						boolean generating = true;
						while(generating){
							govtLegitimacy = random.nextDouble();
							if((govtLegitimacy > govtLegitimacyMin2) && (govtLegitimacy < govtLegitimacyMax2)){
								generating = false;
							}
						}
					}
					agent = new Civilian(new Int2D(x, y), riskAversion, perceivedHardship, govtLegitimacy);
					quietCount++;
					schedule.scheduleRepeating(agent);
					numCivilians2Counter++;
				}
				
				Agent a;
				int j, k;
				boolean placing = true;
				while(placing){
					j = random.nextInt(gridWidth);
					k = random.nextInt(gridHeight);
					
					if(grid.getObjectsAtLocation(j, k) == null){
						grid.setObjectLocation(agent, j, k);
						
						if(agent != null){
							a = (Agent)agent;
							a.loc = new Int2D(j, k);							
						}						
						placing = false;
					}			
				}
			}			
		}
		
		//this schedule will reduce the jail term for all jailed agents after all agents have made their step
		Steppable jailTermReducer = new JailTermReducer();
		schedule.scheduleRepeating(0, 1, jailTermReducer);
		
		
		/*print out averages*/
		double grievanceAvg = 0;
		ArrayList<Double> grievances = new ArrayList<Double>();
		double perceivedHardshipAvg = 0;
		ArrayList<Double> perceivedHardships = new ArrayList<Double>();
		double riskAversionAvg = 0;
		ArrayList<Double> riskAversions = new ArrayList<Double>();
		
		Bag agents = grid.allObjects;
		for(Object o : agents){
			Agent a = (Agent)o;
			
			if(a instanceof Civilian){
				grievances.add(((Civilian) a).getGrievance());
				perceivedHardships.add(((Civilian) a).getPerceivedHardship());
				riskAversions.add(((Civilian) a).getRiskAversion());
			}
		}
		
		for(Double d : grievances){
			grievanceAvg += d;
		}
		grievanceAvg = grievanceAvg/grievances.size();
		
		for(Double d : perceivedHardships){
			perceivedHardshipAvg += d;
		}
		perceivedHardshipAvg = perceivedHardshipAvg/perceivedHardships.size();
		
		for(Double d : riskAversions){
			riskAversionAvg += d;
		}
		riskAversionAvg = riskAversionAvg/riskAversions.size();
		
		System.out.println("Average Grievance:\t\t" + grievanceAvg);
		System.out.println("Average Perceived Hardship:\t" + perceivedHardshipAvg);
		System.out.println("Average Risk Aversion:\t\t" + riskAversionAvg + "\n");
	}



	public static void main(String[] args){
		doLoop(EpsteinGrid.class, args);
		System.exit(0);
	}
	

	public int getGridHeight() {return gridHeight;}
	public void setGridHeight(int gridHeight) {this.gridHeight = gridHeight;}
	public int getGridWidth() {return gridWidth;}
	public void setGridWidth(int gridWidth) {this.gridWidth = gridWidth;}
	public int getNumCivilians1() {return numCivilians1;}
	public void setNumCivilians1(int numCivilians1) {this.numCivilians1 = numCivilians1;}
	public int getNumCivilians2() {return numCivilians2;}
	public void setNumCivilians2(int numCivilians2) {this.numCivilians2 = numCivilians2;}
	public int getNumCops() {return numCops;}
	public void setNumCops(int numCops) {this.numCops = numCops;}
	public double getGovtLegitimacyMin1() {return govtLegitimacyMin1;}
	public void setGovtLegitimacyMin1(double govtLegitimacyMin) {this.govtLegitimacyMin1 = govtLegitimacyMin;}
	public double getGovtLegitimacyMax1() {return govtLegitimacyMax1;}
	public void setGovtLegitimacyMax1(double govtLegitimacyMax) {this.govtLegitimacyMax1 = govtLegitimacyMax;}
	public double getGovtLegitimacyMin2() {return govtLegitimacyMin2;}
	public void setGovtLegitimacyMin2(double govtLegitimacyMin2) {this.govtLegitimacyMin2 = govtLegitimacyMin2;}
	public double getGovtLegitimacyMax2() {return govtLegitimacyMax2;}
	public void setGovtLegitimacyMax2(double govtLegitimacyMax2) {this.govtLegitimacyMax2 = govtLegitimacyMax2;}
	public int getMaxJailTerm() {return maxJailTerm;}
	public void setMaxJailTerm(int maxJailTerm) {this.maxJailTerm = maxJailTerm;}
	public double getThreshold() {return threshold;}
	public void setThreshold(double threshold) {this.threshold = threshold;}
	public int getCivilianVision() {return civilianVision;}
	public void setCivilianVision(int civilianVision) {this.civilianVision = civilianVision;}
	public int getCopVision() {return copVision;}
	public void setCopVision(int copVision) {this.copVision = copVision;}
	public int getActiveCount() {return activeCount;}
	public void setActiveCount(int activeCount) {this.activeCount = activeCount;}
	public int getQuietCount() {return quietCount;}
	public void setQuietCount(int quietCount) {this.quietCount = quietCount;}
	public int getJailCount() {return jailCount;}
	public void setJailCount(int jailCount) {this.jailCount = jailCount;}
	public boolean isMovement() {return movement;}
	public void setMovement(boolean movement) {this.movement = movement;}
	public boolean isSmartMovement() {return smartMovement;}
	public void setSmartMovement(boolean smartMovement) {this.smartMovement = smartMovement;}	
	public double getGroup() {return group;}
	public void setGroup(double group) {this.group = group;}
}
